// 
//  
//  Copyright (C) 2004-2009  Autodesk, Inc.
//  
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of version 2.1 of the GNU Lesser
//  General Public License as published by the Free Software Foundation.
//  
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//  
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//  
#include "stdafx.h"
#include "MapInfoConnection.h"
#include "MapInfoFeatureReader.h"
#include "OgrFdoUtil.h"
#include "MapInfoProvider.h"
#include "FdoSpatial.h"
#include "MapInfoClassDefinition.h"

#include <FdoCommonMiscUtil.h>
#include <FdoCommonSchemaUtil.h>

//---------------------------------------------------------------------
//
//    MapInfoFeatureReader
//
//---------------------------------------------------------------------

MapInfoFeatureReader::MapInfoFeatureReader(
    MapInfoConnection* connection,
    MapInfoClassDefinition* mapinfoClassDef,
    FdoIdentifierCollection* selectedProps,
    recno_list* features)
{
    _ASSERT(mapinfoClassDef);
    m_idPropName = mapinfoClassDef->GetIdentityPropertyName();
    m_geomPropName = mapinfoClassDef->GetGeometryPropertyName();

    FdoPtr<FdoClassDefinition> classDef = mapinfoClassDef->GetFdoClass();
    m_class = FdoCommonSchemaUtil::DeepCopyFdoClassDefinition(classDef);
    m_class->AddRef();

    m_selectedProps = FDO_SAFE_ADDREF(selectedProps);


    FdoPtr<FdoPropertyDefinitionCollection> props = m_class->GetProperties();
    for (FdoInt32 i = 0; i < props->GetCount(); ++i)
    {
        FdoPtr<FdoPropertyDefinition> propDef = props->GetItem(i);
        if (propDef->GetPropertyType() == FdoPropertyType_DataProperty)
        {
            FdoDataPropertyDefinition* dataPropDef = dynamic_cast<FdoDataPropertyDefinition*>(propDef.p);
            m_propDataTypeMap[propDef->GetName()] = dataPropDef->GetDataType();
        }
        else
            m_propDataTypeMap[propDef->GetName()] = (FdoDataType)(-1);
    }

    _ASSERT(connection);
    m_connection = connection;
    m_connection->AddRef();

    m_poLayer = mapinfoClassDef->GetMapInfoFile();
    m_poLayer->ResetReading();
    m_poFeature = NULL;

    m_pFeatures = features;
    m_nCurrentFeature = 0;
    
    m_fgflen = 64;
    m_fgf = new unsigned char[m_fgflen*2];
    m_wkb = new unsigned char[m_fgflen];
}

MapInfoFeatureReader::~MapInfoFeatureReader()
{
    Close();
    FDO_SAFE_RELEASE(m_connection);
    FDO_SAFE_RELEASE(m_class);
    FDO_SAFE_RELEASE(m_selectedProps);

    delete [] m_fgf;
    delete [] m_wkb;

    if(m_pFeatures)
        delete m_pFeatures;
}

void MapInfoFeatureReader::Dispose()
{
    delete this;
}

FdoClassDefinition* MapInfoFeatureReader::GetClassDefinition()
{
    return FDO_SAFE_ADDREF(m_class);
}

FdoInt32 MapInfoFeatureReader::GetDepth()
{
    return 0;
}

bool MapInfoFeatureReader::GetBoolean(FdoString* propertyName)
{
    CheckIfPropExists(propertyName);

    W2A(propertyName);
    return m_poFeature->GetFieldAsBoolean(mbpropertyName);
}

FdoByte MapInfoFeatureReader::GetByte(FdoString* propertyName)
{
    throw FdoException::Create (NlsMsgGetMain(MAPINFOPROVIDER_DATA_TYPE_NOT_SUPPORTED, "The '%1$ls' data type is not supported by MapInfo.", FdoCommonMiscUtil::FdoDataTypeToString (FdoDataType_Byte)));
}

FdoDateTime MapInfoFeatureReader::GetDateTime(FdoString* propertyName)
{
    CheckIfPropExists(propertyName);

    W2A(propertyName);
    int yr = -1;
    int mt = -1;
    int dy = -1;
    int hr = -1;
    int mn = -1;
    int sc = -1;
    int tz = -1;
    
    int index = m_poFeature->GetFieldIndex(mbpropertyName);
    m_poFeature->GetFieldAsDateTime(index, &yr, &mt, &dy, &hr, &mn, &sc, &tz);
    
    return FdoDateTime(yr, mt, dy, hr, mn, (sc==-1) ? 0.0f: (float)sc);
}

double MapInfoFeatureReader::GetDouble(FdoString* propertyName)
{
    CheckIfPropExists(propertyName);

    W2A(propertyName);
    return m_poFeature->GetFieldAsDouble(mbpropertyName);
}

FdoInt16 MapInfoFeatureReader::GetInt16(FdoString* propertyName)
{
    CheckIfPropExists(propertyName);

    W2A(propertyName);
    return m_poFeature->GetFieldAsSmallInteger(mbpropertyName);
}

FdoInt32 MapInfoFeatureReader::GetInt32(FdoString* propertyName)
{
    CheckIfPropExists(propertyName);

    W2A(propertyName);
    
    //check if we are asked for ID property
    if (m_idPropName == propertyName)
        return m_poFeature->GetFID();
     
    return m_poFeature->GetFieldAsInteger(mbpropertyName);
}

FdoInt64 MapInfoFeatureReader::GetInt64(FdoString* propertyName)
{
    throw FdoCommandException::Create (NlsMsgGetMain(MAPINFOPROVIDER_DATA_TYPE_NOT_SUPPORTED, "The '%1$ls' data type is not supported by MapInfo.", FdoCommonMiscUtil::FdoDataTypeToString (FdoDataType_Int64)));
}

float MapInfoFeatureReader::GetSingle(FdoString* propertyName)
{
    W2A(propertyName);
    return m_poFeature->GetFieldAsFloat(mbpropertyName);
}

FdoString* MapInfoFeatureReader::GetString(FdoString* propertyName)
{
    W2A(propertyName);
    const char* val = m_poFeature->GetFieldAsString(mbpropertyName);
    
    A2W(val);
    m_sprops[(long)val] = wval;
    int i = m_sprops[(long)val].size() - 1;
    for(; i >= 0; i --)
    {
        if(val[i] != ' ')
            break;
    }
    m_sprops[(long)val].erase(i + 1, m_sprops[(long)val].size() - (i + 1));
    return m_sprops[(long)val].c_str();
}

FdoLOBValue* MapInfoFeatureReader::GetLOB(FdoString* propertyName)
{
    throw FdoCommandException::Create(NlsMsgGetMain(FDO_NLSID(MAPINFOPROVIDER_LOBS_NOT_SUPPORTED)));
}

FdoIStreamReader* MapInfoFeatureReader::GetLOBStreamReader(FdoString* propertyName )
{
    throw FdoCommandException::Create(NlsMsgGetMain(FDO_NLSID(MAPINFOPROVIDER_LOBS_NOT_SUPPORTED)));
}

bool MapInfoFeatureReader::IsNull(FdoString* propertyName)
{
    W2A(propertyName);
    
    //check if we are asked for ID property
    if (m_idPropName == propertyName)
        return false;

    //check if it is the geom property
    if (m_geomPropName == propertyName)
        return (m_poFeature->GetGeometryRef() == NULL);
    
    return !m_poFeature->IsFieldSet(m_poFeature->GetFieldIndex(mbpropertyName));
}

FdoIFeatureReader* MapInfoFeatureReader::GetFeatureObject(FdoString* propertyName)
{
    throw FdoCommandException::Create(NlsMsgGetMain(FDO_NLSID(MAPINFOPROVIDER_OBJECT_PROPERTY)));
}

FdoByteArray* MapInfoFeatureReader::GetGeometry(FdoString* propertyName)
{
    int len = 0;
    const void* ptr = GetGeometry(propertyName, &len);
    return FdoByteArray::Create((unsigned char*)ptr, len);
}

const FdoByte* MapInfoFeatureReader::GetGeometry(OGRGeometry* geom, FdoInt32* len)
{
    if (geom)
    {    
        size_t wkblen = geom->WkbSize();
        
        //allocate enough to hold the geom array
        if (m_fgflen < wkblen)
        {
            delete [] m_fgf;
            delete [] m_wkb;
            m_fgflen = wkblen;
            m_fgf = new unsigned char[m_fgflen*2];
            m_wkb = new unsigned char[m_fgflen];
        }
        
        geom->exportToWkb(wkbNDR, (unsigned char*)m_wkb);
        
        *len = OgrFdoUtil::Wkb2Fgf(m_wkb, m_fgf);
        return (const unsigned char*)m_fgf;
    }

    throw FdoException::Create(L"Geometry is null.");
}

const FdoByte* MapInfoFeatureReader::GetGeometry(FdoString* propertyName, FdoInt32* len)
{
    return this->GetGeometry(m_poFeature->GetGeometryRef(), len);
}

FdoIRaster* MapInfoFeatureReader::GetRaster(FdoString* propertyName)
{
    return NULL;
}

bool MapInfoFeatureReader::ReadNext()
{
    try
    {
        m_sprops.clear();
        
        if (m_poFeature)
        {
            OGRFeature::DestroyFeature(m_poFeature);
            m_poFeature = NULL;
        }
        
        int nFeatureCount = 0;
        if(m_pFeatures)
        {
            nFeatureCount = m_pFeatures->size();
            while (m_nCurrentFeature < nFeatureCount)
            {
                m_poFeature = dynamic_cast<TABFeature*>(m_poLayer->GetFeature((*m_pFeatures)[m_nCurrentFeature++]));
                if (m_poFeature)
                {
                    if (m_poFeature->IsRecordDeleted())
                    {
                        OGRFeature::DestroyFeature(m_poFeature);
                        m_poFeature = NULL;
                    }
                    else
                        break;
                }
            }
        }
        else
        {
            nFeatureCount = m_poLayer->GetFeatureCount(TRUE);
            while (m_nCurrentFeature < nFeatureCount)
            {
                m_poFeature = dynamic_cast<TABFeature*>(m_poLayer->GetFeature(++m_nCurrentFeature));
                if (m_poFeature)
                {
                    if (m_poFeature->IsRecordDeleted())
                    {
                        OGRFeature::DestroyFeature(m_poFeature);
                        m_poFeature = NULL;
                    }
                    else
                        break;
                }
            }
        }

        return (m_poFeature != NULL && m_nCurrentFeature <= nFeatureCount);
    }
    catch(...)
    {
        return false;
    }
}

void MapInfoFeatureReader::Close()
{
    if (m_poFeature)
    {
        OGRFeature::DestroyFeature(m_poFeature);
        m_poFeature = NULL;
    }
    delete m_pFeatures;
    m_pFeatures = NULL;
}

FdoDataType MapInfoFeatureReader::GetDataType( FdoString* propertyName )
{
    CheckIfPropExists(propertyName);

    FdoDataType dt = m_propDataTypeMap[propertyName];
    if (dt == -1)
        throw FdoCommandException::Create(NlsMsgGetMain(MAPINFOPROVIDER_NOT_DATA_PROPERTY, "Property '%1$ls' is a data property.", propertyName));

    return dt;
}

void MapInfoFeatureReader::CheckIfPropExists(FdoString* propertyName)
{
    if (m_propDataTypeMap.end() == m_propDataTypeMap.find(propertyName))
        throw FdoCommandException::Create(NlsMsgGetMain(MAPINFOPROVIDER_INVALID_PROPERTY_NAME_ARG, "Invalid property name: %1$ls.", propertyName));
    
    
    // The next few lines are commented. It is because that the expression engine will try to get the value of properties to
    // evaluate filters. If we throw exception from here, then it won't be able to evaluate filters that reference properties
    // that are not in selected property list.
    // A side effect is that user can get un-selected propeties through reader::Get...  SDF also behave this way.
    
    //if(m_selectedProps && !m_selectedProps->Contains(propertyName))
    //{
    //    throw FdoCommandException::Create(NlsMsgGetMain(MAPINFOPROVIDER_INVALID_PROPERTY_NAME_ARG, "Invalid property name: %1$ls.", propertyName));
    //}
}

